﻿using System; 
using System.Windows.Forms;

using HongsLibrary.Test;
using HongsLibrary.Common;
using HongsLibrary.Core.OpenGL;
using Tao.OpenGl;      

namespace HongsLibrary.Test
{
    public partial class MainForm : Form
    {
        #region Members
        private Test _currentTest;
        public Test CurrentTest
        {
            get { return _currentTest; }
            set { _currentTest = value; }
        }

        private float _viewZoom = 1f;
        private Vector2 _viewCenter = new Vector2(0.0f, 20.0f);

        private bool _rMouseDown = false;
        private Vector2 _lastp;

        private Settings _settings = new Settings();
        private TestEntry _testEntry;


        #endregion Members

        public MainForm()
        {
            InitializeComponent();

            this._openGlControl.InitializeContexts();
            OpenGLDebugDraw.InitTextRenderer(this._openGlControl);

            InitGL();
            SetView();

        }

        private void MainForm_Load(object sender, EventArgs e)
        {
            this._testEntry = Test.g_testEntries[0];

            CurrentTest = this._testEntry.CreateFcn();

            redrawTimer.Interval = 16;
            redrawTimer.Enabled = true;
        }


        private void SetView()
        {
            int width = this._openGlControl.Width;
            int height = this._openGlControl.Height;

            Gl.glViewport(0, 0, width, height);
            Gl.glMatrixMode(Gl.GL_PROJECTION);
            Gl.glLoadIdentity();

            float ratio = (float)width / (float)height;

            Vector2 extents = new Vector2(ratio * 25.0f, 25.0f);
            extents *= this._viewZoom;

            Vector2 lower = this._viewCenter - extents;
            Vector2 upper = this._viewCenter + extents;

            // L/R/B/T
            Glu.gluOrtho2D(lower.X, upper.X, lower.Y, upper.Y);

            Gl.glMatrixMode(Gl.GL_MODELVIEW);
            Gl.glLoadIdentity();

        }

        private void InitGL()
        {
            Gl.glShadeModel(Gl.GL_SMOOTH);
            Gl.glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
            Gl.glClearDepth(1.0f);
            Gl.glEnable(Gl.GL_COLOR_MATERIAL);
            Gl.glEnable(Gl.GL_LIGHT0);
            Gl.glHint(Gl.GL_PERSPECTIVE_CORRECTION_HINT, Gl.GL_NICEST);
        }

        private void redrawTimer_Tick(object sender, EventArgs e)
        {
            SimulationLoop();
        }


        private void SimulationLoop()
        {
            Gl.glClear(Gl.GL_COLOR_BUFFER_BIT | Gl.GL_DEPTH_BUFFER_BIT);

            CurrentTest.SetTextLine(30);
            CurrentTest.Step(this._settings);
            OpenGLDebugDraw.DrawString(5, 15, _testEntry.Name);


            _openGlControl.Draw();

            int errorCode = 0;
            if ((errorCode = Gl.glGetError()) > 0)
            {
                redrawTimer.Stop();
            }
        }

        private void _openGlControl_MouseDown(object sender, MouseEventArgs e)
        {
            if (e.Button == MouseButtons.Left)
                CurrentTest.MouseDown(ConvertScreenToWorld(e.X, e.Y));
            else if (e.Button == MouseButtons.Right)
            {
                _lastp = ConvertScreenToWorld(e.X, e.Y);
                _rMouseDown = true;
            }
        }

        private void _openGlControl_MouseMove(object sender, MouseEventArgs e)
        {
            Vector2 p = ConvertScreenToWorld(e.X, e.Y);
            CurrentTest.MouseMove(p);

            if (_rMouseDown)
            {
                Vector2 diff = p - _lastp;
                _viewCenter.X -= diff.X;
                _viewCenter.Y -= diff.Y;
                SetView();
                _lastp = ConvertScreenToWorld(e.X, e.Y);
            }
        }

        private void _openGlControl_MouseUp(object sender, MouseEventArgs e)
        {
            if (e.Button == MouseButtons.Left)
                CurrentTest.MouseUp();
            else if (e.Button == MouseButtons.Right)
                _rMouseDown = false;
        }

        protected override void OnMouseWheel(MouseEventArgs e)
        {
            if (e.Location.X > _openGlControl.Location.X && e.Location.X < _openGlControl.Location.X + _openGlControl.Width &&
                e.Location.Y > _openGlControl.Location.Y && e.Location.Y < _openGlControl.Location.Y + _openGlControl.Height)
            {
                if (e.Delta > 0)
                {
                    _viewZoom /= 1.1f;
                }
                else
                {
                    _viewZoom *= 1.1f;
                }
                SetView();
            }
        }



        private Vector2 ConvertScreenToWorld(float x, float y)
        {
            float tw = _openGlControl.Width;
            float th = _openGlControl.Height;
            float u = x / tw;
            float v = (th - y) / th;

            float ratio = tw / th;
            Vector2 extents = new Vector2(ratio * 25.0f, 25.0f);
            extents *= this._viewZoom;

            Vector2 lower = this._viewCenter - extents;
            Vector2 upper = this._viewCenter + extents;

            Vector2 p = new Vector2();
            p.X = (1.0f - u) * lower.X + u * upper.X;
            p.Y = (1.0f - v) * lower.Y + v * upper.Y;
            return p;
        }

        private void _openGlControl_PreviewKeyDown(object sender, PreviewKeyDownEventArgs e)
        {
            switch (e.KeyCode)
            {
                case Keys.Escape:
                    this.Close();
                    break;
                case Keys.Z:
                    _viewZoom = Common.Math.Min(1.1f * _viewZoom, 20.0f);
                    SetView();
                    break;
                case Keys.X:
                    _viewZoom = Common.Math.Max(0.9f * _viewZoom, 0.02f);
                    SetView();
                    break;
                case Keys.R:
                    CurrentTest = _testEntry.CreateFcn();
                    break;
                case Keys.Space:
                    CurrentTest.LaunchBomb();
                    break;
                case Keys.Left:
                    _viewCenter.X -= 0.5f;
                    SetView();
                    break;
                case Keys.Right:
                    _viewCenter.X += 0.5f;
                    SetView();
                    break;
                case Keys.Down:
                    _viewCenter.Y -= 0.5f;
                    SetView();
                    break;
                case Keys.Up:
                    _viewCenter.Y += 0.5f;
                    SetView();
                    break;
                case Keys.Home:
                    _viewZoom = 1.0f;
                    _viewCenter.Set(0.0f, 20.0f);
                    SetView();
                    break;
                default:
                    CurrentTest.Keyboard(e.KeyCode);
                    break;
            }
        }


    }
}
